/** 
 * \file   Logging.h
 * \author T. M. Jandreau
 *
 * \date December 16, 2012, 10:34 AM
 * 
 * This file contains the function and class defintions for logging utilities.
 */

#ifndef LOGGING_H
#define	LOGGING_H

#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <ace/Singleton.h>
#include <ace/Log_Record.h>
#include <ace/Log_Msg.h>
#include <ace/Log_Msg_Callback.h>

#define DEBUG_PREFIX            ACE_TEXT("DEBUG (%T)%I: ")
#define INFO_PREFIX             ACE_TEXT("INFO  (%T)%I: ")
#define ERROR_PREFIX            ACE_TEXT("ERROR (%T)%I: ")

#define DEBUG(FMT, ...) ACE_DEBUG((LM_DEBUG, DEBUG_PREFIX ACE_TEXT(FMT), ##__VA_ARGS__))
#define INFO(FMT,...) ACE_DEBUG((LM_INFO, INFO_PREFIX ACE_TEXT(FMT),  ##__VA_ARGS__ ))
#define ERROR(FMT,...) ACE_DEBUG((LM_ERROR, ERROR_PREFIX ACE_TEXT(FMT), ##__VA_ARGS__))

namespace UTIL {

    /**
     * \class Logging
     * \brief This class publically inherits ACE_Log_Msg_Calback class and 
     * evokes the log message callback handler. 
     * 
     * When the static functions above are
     * called, an input vector is generated and then the wildcards and expanded 
     * command line parameters are evaluated. 
     */
    class Logging : public ACE_Log_Msg_Callback {
        
    public:

        /**
         * \brief Updates and sets the log output directory
         * 
         * When called, this function will update the output directory and then
         * rotate the logs if the input differs from the existing output directory
         * file path name.
         * 
         * @param outputDir string holding the output directory
         */
        void setOutputDirectory(std::string &outputDir);

        ///\brief Function rotates the logs and increments the sequence parameter
        void rotateLogs();

        /**
         * \brief Default constructor for Logging
         * 
         * Sets the default directory and initializes the logs. It also updates
         * and ACE flag to let the system know it should be using our log callback
         * pointer
         */
        Logging();


        /**
         * \brief Default destructor for Logging
         * 
         * This function clears the log callback flags and then shuts down the logs.
         */
        virtual ~Logging();

        
    private:

        /**
         * \brief Virtual method inherited from ACE_Log_Msg_Callback
         * 
         * This function must be overwritten as it is pure virtual. This is the 
         * callback handler function. In this case, it obtains the ACE_Log_Record,
         * dumps the status to the screen and then writes the output to a particular
         * file.
         * 
         * @param logRecord Log Record of the ace callback message
         */
        virtual void log(ACE_Log_Record &logRecord);

        /**
         * \brief Function assigns fileMap keys for potential map callback files
         */
        void openLogs();

        /**
         * \brief Function closes fileMap iterated value and closes the callback files.
         */
        void closeLogs();

        ///\brief Generates filename based on outputDirectory, sequence enumeration
        /// and desired suffix
        std::string generateFilename(std::string suffix);

        ///\brief Private output directory string
        std::string outputDir;

        ///\brief Sequencing for the numbering system
        int sequence;

        /**
         * \class Files
         * \brief This class is a wrapper facade around low-level file i/o
         * 
         */
        class Files {
        public:

            /**
             * \brief Default constructor
             * 
             * This function initializes the file stream.
             * 
             * @param filename Current filename to write data to
             */
            Files(std::string filename) {
                fStream.open(filename.c_str(), ios::out);
                if (!fStream.is_open()) {
                    ERROR("Unable to open %s\n", filename.c_str());
                }
            }

            ///\brief Default constructor. Resets the files.

            ~Files() {
                reset();
            }

            ///\brief Write string data to the file stream output

            void write(std::string data) {
                mutex.acquire();
                fStream << data.c_str();
                mutex.release();
            }

            ///\brief Closes the file stream operator

            void reset() {
                mutex.acquire();
                fStream.close();
                mutex.release();
            }

        private:

            ///\brief Mutex defined for write protection while closing
            ACE_Thread_Mutex mutex;

            ///\brief Private file stream member
            std::fstream fStream;
        };

        /**
         * \brief Member type to support the file map
         * 
         * The reason a file map is the preferred method is because it is
         * easily extensible. We can easily change/add a key value and a new
         * value pointer to the openLogs() function.
         */
        std::map< unsigned long, Files* > fileMap;

    };

} // namespace UTIL

#endif	/* LOGGING_H */

